package cn.xwt.weidisk.service.impl;

import cn.xwt.weidisk.mapper.FileMapper;
import cn.xwt.weidisk.pojo.dao.FileDo;
import cn.xwt.weidisk.pojo.dao.RecycleFileDo;
import cn.xwt.weidisk.pojo.dto.*;
import cn.xwt.weidisk.service.FileService;
import cn.xwt.weidisk.util.CosUtils;
import cn.xwt.weidisk.util.DiskUtils;
import cn.xwt.weidisk.util.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.util.*;

/**
 * @author 薛慰涛
 */
@Service("systemService")
public class FileServiceImpl implements FileService {

    private final String ROOT = "/";
    private final String FOLDER = "folder";

    Logger logger = LoggerFactory.getLogger(getClass());

    @Resource
    FileMapper fileMapper;

    @Resource
    FileUtils fileUtils;

    @Resource
    DiskUtils diskUtils;


    @Override
    public Resp getFileUploadUrl(FileDo fileDo) {
        // 文件已存在
        if (fileUtils.exists(fileDo)) {
            return new Resp("A0700", "文件已存在");
        }
        // 文件信息保存失败
        if (1 != fileMapper.insertFile(fileDo)) {
            return new Resp("C0300", "服务器繁忙");
        }
        Resp resp = Resp.success();
        HashMap<String, String> map = new HashMap<>(2);
        map.put("url", CosUtils.getFileUploadUrl(fileDo.getKey()));
        map.put("key", fileDo.getKey());
        resp.setObject(map);
        return resp;
    }

    @Override
    public boolean saveThumbnail(ThumbnailData thumbnail) {
        String base64Url = thumbnail.getBase64Url();
        String data = "";

        try {
            if (base64Url == null || "".equals(base64Url)) {
                throw new Exception("获取base64数据失败");
            } else {
                // 获取base
                String[] d = base64Url.split("base64,");
                if (d.length == 2) {
                    data = d[1];
                } else {
                    throw new Exception("上传失败，数据不合法");
                }
            }
            // 将base64转换为byte数组
            byte[] base64Byte = Base64Utils.decodeFromString(data);
            ByteArrayInputStream inputStream = new ByteArrayInputStream(base64Byte);
            CosUtils.uploadThumbnail(inputStream, thumbnail.getKey());
            inputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
            return true;
        }
        return false;
    }

    @Override
    public boolean insertFolder(FileDo fileDo) {
        if (fileUtils.exists(fileDo)) {
            return false;
        }
        return fileMapper.insertFile(fileDo) == 1;
    }

    @Override
    public String getFileDownloadUrl(String name, String key) {
        String url;
        try {
            url = CosUtils.getFileDownloadUrl(name, key);
        } catch (Exception e) {
            url = null;
            logger.error("获取" + name + "下载路径失败");
        }
        return url;
    }

    @Override
    public List<FileDo> getAllList(Long uid, String path) {
        List<FileDo> folders = fileMapper.selectFolderList(uid, path);
        Collections.reverse(folders);
        List<FileDo> files = fileMapper.selectFileList(uid, path);
        Collections.reverse(files);
        folders.addAll(files);
        return folders;
    }

    @Override
    public boolean updateName(RenameFileData renameFileData) {
        // 判断是否存在同名文件
        FileDo file = new FileDo();
        file.setUid(renameFileData.getUid());
        file.setName(renameFileData.getNewName());
        file.setPath(renameFileData.getPath());
        if (fileUtils.exists(file)) {
            return false;
        }
        // 文件夹类型
        if (renameFileData.getType().equals(FOLDER)) {
            String originalFolderPath = renameFileData.getPath() + renameFileData.getOriginalName();
            String newFolderPath = renameFileData.getPath() + renameFileData.getNewName();
            List<FileDo> subFileList = fileMapper.selectSubList(renameFileData.getUid(), originalFolderPath + "%");
            for (FileDo fileDO : subFileList) {
                String newPath = fileDO.getPath().replaceFirst(originalFolderPath, newFolderPath);
                FileDo pathData = new FileDo();
                pathData.setPath(newPath);
                pathData.setId(fileDO.getId());
                fileMapper.updatePath(pathData);
            }
        }
        // 修改选中的文件名
        fileMapper.updateName(renameFileData.getFid(), renameFileData.getNewName());

        return true;
    }

    @Override
    public List<FileDo> selectChildren(FileDo parentDir) {
        List<FileDo> children = new LinkedList<>();
        // 获取当前文件路径及其子文件路径
        String currentPath;
        String subPath;
        // 如果为根目录则不添加 "/"
        if (parentDir.getPath().equals(ROOT)) {
            currentPath = ROOT + parentDir.getName();
        } else {
            currentPath = parentDir.getPath() + ROOT + parentDir.getName();
        }
        subPath = currentPath + "/%";
        // 查询当前文件夹下的所有文件
        List<FileDo> currentList = fileMapper.selectCurrentList(parentDir.getUid(), currentPath);
        List<FileDo> subList = fileMapper.selectSubList(parentDir.getUid(), subPath);
        // 添加所有子文件到待删除文件
        children.addAll(currentList);
        children.addAll(subList);
        return children;
    }


    @Override
    public void deleteFile(FileDo file) {
        // 添加rid
        fileMapper.updateRidById(file);
        // 如果是文件夹则查询其下所有文件并加入待删除列表
        if (file.getType().equals(FOLDER)) {
            List<FileDo> deleteList = new LinkedList<>(selectChildren(file));
            // 删除要删除的文件
            for (FileDo delete : deleteList) {
                delete.setRid(file.getRid());
                fileMapper.updateRidById(delete);
            }
        }
    }

    @Override
    public FolderTree getFolderTree(Long uid) {
        FolderTree root = new FolderTree();
        root.setName("全部文件");
        root.setPath("/");
        getChildren(root, uid);
        return root;
    }

    private void getChildren(FolderTree root, Long uid) {
        // 查询当前文件夹下的子文件夹
        List<FileDo> folderList = fileMapper.selectFolderList(uid, root.getPath());
        if (folderList == null) {
            // 当前文件夹下没有文件
            root.setChildren(null);
        } else {
            // 当前文件夹下含有文件
            // folderTree添加children
            List<FolderTree> children = new ArrayList<>();
            for (FileDo folder : folderList) {
                FolderTree folderTree = new FolderTree();
                folderTree.setName(folder.getName());
                String path;
                if (folder.getPath().equals(ROOT)) {
                    path = ROOT + folder.getName();
                } else {
                    path = folder.getPath() + "/" + folder.getName();
                }
                folderTree.setPath(path);
                children.add(folderTree);
            }
            root.setChildren(children);
            // 遍历children
            for (FolderTree child : children) {
                getChildren(child, uid);
            }
        }
    }

    @Override
    public Resp moveFile(MoveData moveData, Long uid) {
        // 要移动的文件
        List<FileDo> moveList = new LinkedList<>();
        // 已存在的文件
        List<FileDo> existsList = new LinkedList<>();

        String to = moveData.getTo();
        String from = moveData.getFrom();

        // 查询所有要移动的文件
        for (FileDo file : moveData.getSelectedList()) {
            file.setUid(uid);
            // 如果是文件夹
            if (file.getType().equals(FOLDER)) {
                // 移动后的文件夹已存在
                if (fileUtils.existsFolderAfterMove(to, from, file)) {
                    existsList.add(file);
                    continue;
                } else {
                    // 设置文件夹大小
                    file.setSize(0);
                    // 查询子文件
                    moveList.addAll(selectChildren(file));
                }
            }
            moveList.add(file);
        }

        // 容量是否充足
        Long append = 0L;
        for (FileDo file : moveList) {
            append += file.getSize();
        }
        if (!diskUtils.isEnough(append, uid)) {
            return new Resp("A0604", "容量不足");
        }

        // 修改文件路径为移动后的路径
        fileUtils.setMovePath(to, from, moveList);

        // 遍历要移动的文件
        for (FileDo moveFile : moveList) {
            // 文件已经存在
            if (fileUtils.exists(moveFile)) {
                existsList.add(moveFile);
            } else {
                // 为复制文件
                if (moveData.getCopy()) {
                    moveFile.setId(null);
                    moveFile.setUid(uid);
                    fileMapper.insertFile(moveFile);
                // 剪切文件
                } else {
                    fileMapper.updatePath(moveFile);
                }
            }
        }
        if (existsList.isEmpty()) {
            return Resp.success();
        } else {
            Resp resp = new Resp();
            resp.setCode("A0700");
            resp.setMsg("存在重复文件");
            resp.setObject(existsList);
            return resp;
        }
    }

    @Override
    public List<FileDo> selectListByType(Long uid, String type) {
        type = type + "%";
        return fileMapper.selectListByType(uid, type);
    }

    @Override
    public List<FileDo> selectOtherList(Long uid) {
        return fileMapper.selectOtherList(uid);
    }

    @Override
    public List<ImageData> getImageList(Long uid) {
        List<FileDo> imageList = selectListByType(uid, "image");
        List<ImageData> imageDataList = new LinkedList<>();
        for (FileDo fileDo : imageList) {
            String[] key = fileDo.getKey().split("[.]");
            String thumbnailKey = key[0] + "_thumbnail." + "jpg";
            ImageData imageData = new ImageData(fileDo, CosUtils.getPreviewUrl(thumbnailKey));
            imageDataList.add(imageData);
        }
        return imageDataList;
    }

    @Override
    public String getPreviewUrl(String key) {
        return CosUtils.getPreviewUrl(key);
    }

    @Override
    public FileDo selectFileById(Long id) {
        return fileMapper.selectFileById(id);
    }

    @Override
    public boolean enableRecycle(List<RecycleFileDo> recycleFileList, Long uid) {
        List<FileDo> recycleList = new LinkedList<>();
        for (RecycleFileDo item : recycleFileList) {
            recycleList.addAll(fileMapper.selectFileListByRid(item.getId(), uid));
        }
        Long append = 0L;
        for (FileDo fileDo : recycleList) {
            if (fileDo.getSize() == null) {
                break;
            }
            append += fileDo.getSize();
        }
        return diskUtils.isEnough(append, uid);
    }

    @Override
    public boolean revertFile(FileDo file) {
        if (fileUtils.exists(file)) {
            return false;
        } else {
            fileUtils.createFolder(file.getPath(), file.getUid());
            fileMapper.updateRidByRid(file.getRid());
            return true;
        }
    }

    @Override
    public void deleteFileListComplete(List<Long> ridList, Long uid) {
        final String image = "image";
        LinkedList<FileDo> deleteList = new LinkedList<>();
        for (Long rid : ridList) {
            deleteList.addAll(fileMapper.selectFileListByRid(rid, uid));
            fileMapper.deleteFileListByRid(rid, uid);
        }
        LinkedList<String> keyList = new LinkedList<>();
        for (FileDo file : deleteList) {
            String type = file.getType();
            // 跳过文件夹类型
            if (type.equals(FOLDER)) {
                continue;
            }

            String key = file.getKey();
            // key没有被其它文件使用
            if (fileMapper.selectFileByKey(key).isEmpty()) {
                keyList.add(key);
                // 图片类型添加thumbnail
                if (type.contains(image)) {
                    int index = key.lastIndexOf(".");
                    keyList.add(key.substring(0, index) + "_thumbnail" + ".jpg");
                }
            }
            // 要删除的的key不为空则进行删除操作
            if (!keyList.isEmpty()) {
                CosUtils.deleteObjects(keyList);
            }
        }
    }

    @Override
    public Integer getUsed(Long uid) {
        Long used = fileMapper.selectUsed(uid);
        return used == null ? 0 : Math.round(used / (1024 * 1024));
    }
}
